/********** 序列作为约定界面 **********/
const { fib } = require('./02');
const { isNull, pair, head, tail, list, append } = require('./05');
const { isPair, print } = require('./06');

function map(fn, items) {
  if (isNull(items)) return null;
  return pair(fn(head(items)), map(fn, tail(items)));
}

const isOdd = x => x % 2;
const isEven = x => !isOdd(x);
const add = (x, y) => x + y;
const square = x => x ** 2;

/**
 * 求一棵树 值为奇数的叶子平方和
 * 枚举一棵树的树叶
 * 过滤出其中的奇数
 * 对选出的每一个叶子求平方
 * 累加
 */
function sumOddSquares(tree) {
  if (isNull(tree)) return 0;
  if (!isPair(tree)) return isOdd(tree) ? square(tree) : 0;

  return sumOddSquares(head(tree)) + sumOddSquares(tail(tree));
}
function sumOddSquares2(tree) {
  return accumulate(add, 1, map(square, filter(isOdd, enumerateTree(tree))));
}

function filter(predicate, list) {
  if (isNull(list)) return null;
  if (!predicate(head(list))) return filter(predicate, tail(list));
  return pair(head(list), filter(predicate, tail(list)));
}

/**
 * 前n个偶数斐波那契数
 * 枚举0～n个数
 * 求出每个数的斐波那契数
 * 过滤出其中的偶数
 * 合并
 */
function evenFibs(n) {
  return iter(0);
  function iter(k) {
    if (k > n) return null;
    const fibNumber = fib(k);
    if (isEven(fibNumber)) {
      return pair(fibNumber, iter(k + 1));
    } else {
      return iter(k + 1);
    }
  }
}

function evenFibs3(n) {
  return accumulate(
    pair,
    null,
    filter(isEven, map(fib, enumerateInterval(0, n)))
  );
}
// print(evenFibs3(10));

function evenFibs2(n) {
  if (n < 0) return null;
  const fibNumber = fib(n);
  if (isEven(fibNumber)) {
    return pair(fibNumber, evenFibs2(n - 1));
  } else {
    return evenFibs2(n - 1);
  }
}

function accumulate(op, initial, list) {
  if (isNull(list)) return initial;
  return op(head(list), accumulate(op, initial, tail(list)));
}

function enumerateInterval(low, high) {
  if (low > high) return null;
  return pair(low, enumerateInterval(low + 1, high));
}

function enumerateTree(tree) {
  if (isNull(tree)) return null;
  if (!isPair(tree)) return list(tree);

  return append(enumerateTree(head(tree)), enumerateTree(tail(tree)));
}

// 练习
function listFibSquares(n) {
  //枚举，形变，形变，累积
  return accumulate(pair, null, map(square, map(fib, enumerateInterval(0, n))));
}
// print(listFibSquares(5));

function productOfSquaresOfOddElements(tree) {
  //枚举，过滤奇数，形变，累积
  return accumulate(
    (x, y) => x * y,
    1,
    map(square, filter(isOdd, enumerateTree(tree)))
  );
}
const n = list(1, list(2, list(3, 4)));
// console.log(productOfSquaresOfOddElements(n));

// 作业(0530)
function countLeaves(tree) {
  //枚举，形变，累积
  return accumulate(
    add,
    0,
    map(head => 1, enumerateTree(tree))
  );
}
// console.log(countLeaves(n));

function foldRight(op, initial, list) {
  if (isNull(list)) return initial;
  return op(head(list), foldRight(op, initial, tail(list)));
}

const listExample = list(1, 2, 3);

const div = (x, y) => x / y;

// console.log(foldRight(add, 0, listExample));
// console.log(foldLeft(add, 0, listExample));
// console.log(foldRight(div, 1, listExample));
// console.log(foldLeft(div, 1, listExample));
// print(foldRight(pair, null, listExample));
// print(foldLeft(pair, null, listExample));

function foldLeft(op, initial, list) {
  if (isNull(list)) return initial;
  return foldLeft(op, op(initial, head(list)), tail(list));
}

// (1, 2, 3, 4)
// (2, (1, null))
function reverseWithFoldLeft(list) {
  return foldLeft((x, y) => pair(y, x), null, list);
}
print(reverseWithFoldLeft(list(1, 2, 3, 4)));

function foldRight(op, initial, list) {
  if (isNull(list)) return initial;
  return op(head(list), foldRight(op, initial, tail(list)));
}
// (1,2,3,4)
function reverseWithFoldRight(items) {
  return foldRight((x, y) => append(y, list(x)), null, items);
}
print(reverseWithFoldRight(list(1, 2, 3, 4)));
// op(1, op(2, op(3, op(4, null))))
// (4, (3, null))
